import React, { use, useEffect, useState } from 'react';
import {Modal,Table,Space, Button,TableColumnsType, TableProps,Drawer,ConfigProvider,Input, message,Popconfirm } from 'antd';
import NewsComments from '@/components/comments/NewsComments'

import {useDispatch,useSelector} from 'react-redux'
import {fetchArticleList} from '@/store/modules/articleSlice'
import {useTranslation} from 'react-i18next'
import {updateArticleList} from '@/store/modules/articleSlice'
import {
  articleSearchApi,
  articleDetailApi,
  articleListApi,
  createOrderApi,
  aliPayApi,
  aliPayStatusApi
} from '@/api/requestApi'
import { nanoid } from 'nanoid'
import { useNavigate } from 'react-router-dom';
import DOMPurify from 'dompurify';
import ArticleModal from '@/components/modal/ArticleModal';
// antd中的自带的语言包  中文
import zh_CN from 'antd/lib/locale/zh_CN'  
import en_US from 'antd/locale/en_US';

import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const List: React.FC = () => {
  const [selectionType, setSelectionType] = useState<'checkbox' | 'radio'>('checkbox');
  const [articleContent,setArticleContent] = useState('')
  const [open, setOpen] = useState(false);
  const navigate = useNavigate()
  // 从Input组件中解构  Search子组件
  const { Search } = Input;
  // 从仓库中获取文章列表数据
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const {articleList} = useSelector((state:any)=>{
    return state.articleSlice
  })
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const {antdLanguageType} = useSelector((state:any)=>{
    return state.languageSlice
  })

  const [current,setCurrent] = useState(1)  // 记录当前的页码
  const [pageSize,setPageSize] = useState(3) // 每页显示的数据条数
  const [total,setTotal] = useState(()=>{
    return articleList.length
  }) // 数据总数量

  const {t} = useTranslation()
  const dispatch = useDispatch()
  
  // 获取文章详情的方法
  const getArticleDetailById = async(id:number)=>{
    const {data} = await articleDetailApi(id)
    console.log(data,'文章详情')
    // 存储文章详情
    setArticleContent(data)
    // 展开抽屉
    setOpen(true)
    // // 获取文章支付状态
    // getArticlePayStatus()
  }

  // 文章列表的表格表头
  const columns: TableColumnsType<DataType> = [
    {
      title: t('ID'),
      dataIndex: 'id',
      // reader渲染方法,有两个参数,1.text:当前列的值,2.record:当前行的数据
      render: (text: string) => <a href='https://www.baidu.com'>{text}</a>,
    },
    {
      title: t('title'),
      dataIndex: 'title',
    },
    {
      title: t('createdAt'),
      dataIndex: 'createdAt',
    },
    {
      title: t('action'),
      key: 'action',
      // reader渲染方法,有两个参数,1.text:当前列的值,2.record:当前行的数据
      render: (text, record) => (
        <Space size="middle">
          {/* 获取详情的按钮 */}
          <Button type="primary" onClick={()=>getArticleDetailById(record.id)}>{t('detail')}</Button>
          {/* 删除按钮 */}
          <Popconfirm
            title="删除文章"
            description="确认要删除文章吗?"
            onCancel={()=>{
              console.log('取消')
            }}
            onConfirm={()=>deleteArticleById(record.id)}
            okText="确认"
            cancelText="取消"
          >
            <Button type="primary" danger>{t('delete')}</Button>
          </Popconfirm>
          
          {/* 编辑 */}
          <Button type="primary" onClick={()=>showModal(true,record)}>{t('update')}</Button>
        </Space>
      ),
    },
  ];

  // 删除文章操作
  // 注意:这里的删除由于是伪删除,每次删除之后都会重新渲染数据,所以需要使用useEffect()方法来获取数据,否则会报错,但是我们做新增的时候,需要更新数据,重新回显数据,导致这个未删除删除之后立马有重新渲染了,这不是写法有错误,而是伪删除并不是真正的删除数据,所以每次新增之后,useEffect依赖项数组发生变化都会重新渲染,导致删除失败
  const deleteArticleById = (id:number)=>{
    console.log(id)
    // 因为这里的删除接口被禁用,我们实现一个伪删除  文章列表数据存储 articleList中
    console.log(articleList)
    // 这里的删除的逻辑是:通过点击获取到指定文章的id,然后在articleList这个文章数组列表中进行过滤,也就是把符合这个id的列表项从数组中剔除
    const newArticleList = articleList.filter(item=>item.id !==id)
    // console.log(newArticleList)
    // 删除的时候,重新更新redux中的文章列表数据
    dispatch(updateArticleList(newArticleList))
  }

  useEffect(()=>{

    // 从本地存储中获取订单id 和 文章id
    const orderId = localStorage.getItem('pedndingOrderId')
    const articleId = localStorage.getItem('pedndingArticleId')
    // 调用方法获取文章支付状态 需要传入订单id和文章id
    if(orderId && articleId){
      getArticlePayStatus(orderId,articleId)
    }

    // 初始化的时候加载数据
    // getArticleList()
    dispatch(fetchArticleList())
  },[])

  // 搜索事件
  const onSearch = async (value:string)=>{
    // 对搜索框的数据进行判断,如果搜索关键字为空,需要做提示用户操作
    if(value.trim() === ''){
      message.error('搜索关键字不能为空')
      return 
    }
    // console.log(value)
    const res = await articleSearchApi(value)
    // console.log(res.data.rows,'res')
    // 将接口数传递到redux中,进行数据的修改
    // 组件中如何触发redux中的actions？
    const data = res.data.rows.map(item=>({
      key:item.id,
      id:item.id,
      title:item.title,
      createdAt:item.createdAt
    }))

    dispatch(updateArticleList(data))
  }
  // 控制模态框的显示和隐藏
  const [isModalOpen, setIsModalOpen] = useState(false);
  // isEdit是用来表明当前的操作是新增还是编辑
  const [isEdit, setIsEdit] = useState(false);
  // 保存当前需要编辑的数据
  const [currentData, setCurrentData] = useState<any>({});
  // 控制模态框的显示和隐藏
  const showModal = (edit=false,data) => {
    // 点击编辑按钮,传递一个参数 true  edit形参接受数据,通过edit这个参数来判断是新增还是编辑
    console.log(data,'data')
    setIsModalOpen(true); // 模态框显示
    if(edit){ // true 编辑
      setIsEdit(true)
      setCurrentData(data) // 存储当前需要编辑的数据
    } else { // false 新增
      setIsEdit(false)
    }

  };

  const handleOk = () => {
    setIsModalOpen(false);
  };

  const handleCancel = () => {
    setIsModalOpen(false);
  };

  const [selectedRowKeys, setSelectedRowKeys] = useState([]);

  const rowSelection = {
    selectedRowKeys: selectedRowKeys,
    onChange: (selectedRowKeys, selectedRows) => {
      console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
      setSelectedRowKeys(selectedRowKeys);  //这里存储选中的数据的id值
    },
  };

  // 批量删除
  const handleBulkDelete = () => {
    if (selectedRowKeys.length === 0) return; // 长度为0.表示没有选中项,执行终止

    const newArticleList = articleList.filter(item => !selectedRowKeys.includes(item.id));
    dispatch(updateArticleList(newArticleList)); // 
    setSelectedRowKeys([]); // 清空选中状态
    message.success(`成功删除 ${selectedRowKeys.length} 条数据`);
  };


  // 文章付费的操作流程
  const [purchasedArticles,setPurchasedArticles] = useState(()=>{
    const save = localStorage.getItem('purchasedArticles')  // 这个属性就是用来的判断是否是购买过的
    return save ? JSON.parse(save) : {}
  })


  // 判断是否已经购买了该文章
  const isPurchasedArticle = (article)=> !!purchasedArticles[article?.id]
  
  // 创建文章订单  这个方法就是用来提供订单号的,需要将订单号进行返回
  const articleOrder = async ()=>{
    try {
      const res = await createOrderApi({
        price:9.99,
        subject:"文章解锁"
      })
  
      console.log(res,'订单')
      // 将订单号(订单id)进行返回
      return res.data.data.orderId
      
    } catch (error) {
      message.error('订单创建失败')
    }

  }


  // 创建付费的url地址
  const createPayUrl = async (orderId,articleId)=>{
    try {
      const {data} = await aliPayApi({
        orderNo:orderId, // 订单id
        totalPrice:9.99, // 支付价格
        subject:"解锁文章" // 商品描述
      })

      console.log(data,'付费地址')
      if(data && data.code === 200){
        message.success('创建支付地址成功')
        // 根据支付接口返回的数据中的url跳转页面进行付费操作
        window.open(data.url,"_blank")  // 表示新窗口打开
        localStorage.setItem('pedndingOrderId',orderId)  // 将订单id存储在本地
        localStorage.setItem('pedndingArticleId',articleId)  // 将文章id存储在本地
      }
      
    } catch (error) {
      message.error('创建支付地址失败')
    }
  }


  // 获取支付状态 , 使用的是轮询获取支付状态,在useEffect中获取当前的支付状态
  const getArticlePayStatus = (orderId,articleId)=>{
    // 如何去获取文章的状态呢
    const timeId = setInterval(async ()=>{
      // 获取支付状态
      const { data } = await aliPayStatusApi({
        out_trade_no:orderId
      })

      if(data.code === 200 && data.data.tradeStatus === 'TRADE_SUCCESS'){
        message.success('支付成功')
        setPurchasedArticles((prev)=>{
          // prev表示的是所有文章上次的购买状态
          // [articleId]:true  表示当前这篇文章是否已支付 默认是false
          const newState = {...prev,[articleId]:true}
          // 存储到localStorage中 以json字符串的形式存储
          localStorage.setItem('purchasedArticles',JSON.stringify(newState))
        })
        localStorage.removeItem('pedndingOrderId')  // 需要销毁订单id,安全性考虑
        // 修改状态,这个状态就用来判断指定那篇文章是否已付费
        // 清除定时器
        clearInterval(timeId)
      }

    },1000)

  }

  // 导出数据为Excel
const exportToExcel = () => {
  const workbook = new ExcelJS.Workbook();
  const worksheet = workbook.addWorksheet('文章列表');

  // 表头
  const headers = ['ID', '标题', '创建时间'];
  worksheet.addRow(headers);

  // 数据
  articleList.forEach((article) => {
    worksheet.addRow([article.id, article.title, article.createdAt]);
  });

  // 列宽
  worksheet.columns.forEach((column) => {
    column.width = 20;
  });

  // 导出文件
  workbook.xlsx.writeBuffer().then((data) => {
    const blob = new Blob([data], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    });
    saveAs(blob, '文章列表.xlsx');
  });
};

  return (
    <div>
      <Space>
      <Search placeholder={t('search')} onSearch={onSearch} enterButton />
      <Button type="primary" onClick={()=>showModal()}>添加文章</Button>
      <Button 
        type="primary" 
        danger 
        disabled={selectedRowKeys.length === 0}  // 数组长度为0表示没有选中的行，禁用按钮
        onClick={handleBulkDelete}
      >
        批量删除
      </Button>
      <Button type="primary" onClick={exportToExcel}>导出数据</Button>
      </Space>
      <ConfigProvider locale={antdLanguageType ? zh_CN : en_US}>
        <Table<DataType>
          rowSelection={{ type: selectionType, ...rowSelection }}
          columns={columns}
          // dataSource={dataList}
          dataSource={articleList}
          pagination={{
            current,
            pageSize,
            total,
            showQuickJumper:true,
            showSizeChanger:true,
            pageSizeOptions:[3,6,10],
            onChange:(page,pageSize)=>{
              setCurrent(page)
              setPageSize(pageSize)
            }
          }}
        />
      </ConfigProvider>
      
      {/* 使用抽屉组件进行文章详情的展示 */}
      <Drawer title="文章详情" onClose={()=>setOpen(false)} open={open} width={800}>
        {/* <div>
        {articleContent.content}
        </div> */}
        {isPurchasedArticle(articleContent) ? (
          <div>
            {/* 这里通过DOMPurify插件能够将标签字符串正确渲染在页面中实现具体的效果 */}
            <div dangerouslySetInnerHTML={{__html:DOMPurify.sanitize(articleContent.content)}} ></div>
            <p>创建时间：{articleContent.createdAt}</p>
            <p>阅读量：{articleContent.viewCount}</p>
            <p>评论数：{articleContent.comments?.length}</p>
            {/* 回显评论内容 */}
            <NewsComments 
              getArticleDetailById={getArticleDetailById}  // 获取文章详情的方法
              comments={articleContent.comments}  // 文章评论的列表
              articleId={articleContent.id} // 文章id
              ></NewsComments>
              </div>
        ) : (
          <div>
            <Button type="primary" onClick={
              async ()=>{
                const orderId = await articleOrder() // 返回一个订单id
                await createPayUrl(orderId,articleContent.id)  // 在获取url地址方法中传入两个参数,订单id,文章id
              }
            }>付费文章,9.99¥</Button>
          </div>
        )}
      </Drawer>
      {/* 模态框 */}
      <Modal
        title={isEdit ? '编辑文章' : '新增文章'}
        closable={{ 'aria-label': 'Custom Close Button' }}
        open={isModalOpen}
        onOk={handleOk}
        onCancel={handleCancel}
      >

          <ArticleModal 
            isEdit={isEdit}
            currentData={currentData}
            handleCancel={handleCancel}
            ></ArticleModal>

      </Modal>
          
    </div>
  );
};

export default List;

/* 
  一般来讲,涉及到路由跳转对应的组件一般放置在(views/pages) 路由组件
  而对于一些公共组件或者是父组件中的子组件,不涉及路由跳转,一般放置在 componetns文件夹下
  注意:在命名的时候也要注意规范化,命名要语义化  get  set  add  update  delete 


  支付逻辑的思路
    1.有三个接口  创建订单的接口,支付地址的接口,支付状态的接口
    2.首先创建订单,生成订单号(订单id)
    3.支付地址的接口需要使用订单id作为参数,返回值中包含了支付地址(支付url)
    4.获取支付状态的接口也需要订单id 在返回结果中,"tradeStatus": "TRADE_SUCCESS",//交易状态

    问题:如何对这个支付状态进行处理呢?  这里采用的方式是使用定时器轮询的方式,当支付完成后,回到项目文章列表页,再次获取详情查看是否已支付,支付完成之后需要将支付状态存储在本地???? 存储指定文章的id,值为布尔类型






*/